package com.msb.bf;

import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: zhaoyi
 * @create: 2020-05-10 12:44
 * @description: lockSupport版本的A1B2交替打印
 **/

public class LockSupportAB {


    private char[] chars = {'a', 'b', 'c', 'd', 'e'};
    private int[] ints = {1, 2, 3, 4, 5};

    /**
     * 使用 LockSupport
     */
    static Thread t1, t2 = null; //只能是static的才能

    @Test
    public void test() throws InterruptedException {
        t1 = new Thread(() -> {
            for (char aChar : chars) {
                System.out.println(aChar);
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        });
        t2 = new Thread(() -> {
            for (int anInt : ints) {
                LockSupport.park();
                System.out.println(anInt);
                LockSupport.unpark(t1);
            }
        });

        t2.start();
        t1.start();
        TimeUnit.SECONDS.sleep(5);
        System.out.println(t1.getState());
        System.out.println(t2.getState());
    }

    /**
     * 使用 wait/notify
     * 这是一个错误的示例
     * 错误1 当线程2先运行起来的话bug
     * 错误2 循环完应该notify， 可能停不下来线程
     */
    @Test
    public void test2() throws InterruptedException {
        final Object lock = new Object();
        //这是一个错误的示例
        t1 = new Thread(() -> {
            for (int anInt : ints) {
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(anInt);
                    lock.notify();

                }
            }
        });
        t2 = new Thread(() -> {
            for (char aChar : chars) {
                synchronized (lock) {
                    System.out.println(aChar);
                    lock.notify();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        t2.start();
        t1.start();

        TimeUnit.SECONDS.sleep(3);
        System.out.println(t1.getState());
        System.out.println(t2.getState());
    }

    /**
     * 正确的 wait/notify
     */
    @Test
    public void test22() throws InterruptedException {

        final Object lock = new Object();

        //加上这个来确保先打印字母
        CountDownLatch latch = new CountDownLatch(1);

        t1 = new Thread(() -> {
            synchronized (lock) {
                latch.countDown();
                for (char aChar : chars) {
                    System.out.println(aChar);
                    lock.notify();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //循环完要 唤醒，不然其他wait线程不能正常结束
                lock.notify();
            }
        });

        t2 = new Thread(() -> {

            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lock) {
                for (int aChar : ints) {
                    System.out.println(aChar);
                    lock.notify();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //循环完要 唤醒，不然其他wait线程不能正常结束
                lock.notify();
            }
        });


        t2.start();
        t1.start();

        TimeUnit.SECONDS.sleep(3);
        System.out.println(t1.getState());
        System.out.println(t2.getState());

    }


    /**
     * 使用 AtomicXXX+循环
     */
    @Test
    public void test3() throws InterruptedException {
        AtomicBoolean flag = new AtomicBoolean(true);
        t1 = new Thread(() -> {
            for (char aChar : chars) {
                while (true) {
                    if (flag.get()) {
                        System.out.println(aChar);
                        flag.set(false);
                        break;
                    }
                }
            }
        });

        t2 = new Thread(() -> {
            for (int anInt : ints) {
                while (true) {
                    if (!flag.get()) {
                        System.out.println(anInt);
                        flag.set(true);
                        break;
                    }
                }
            }
        });

        t1.start();
        t2.start();

        TimeUnit.SECONDS.sleep(5);

        System.out.println(t1.getState());
        System.out.println(t2.getState());

    }

    /**
     * Condition
     * 很类似 wait/notify
     */
    @Test
    public void test4() throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        t1 = new Thread(() -> {
            try {
                lock.lock();
                for (char aChar : chars) {
                    System.out.println(aChar);
                    condition.signal();
                    condition.await();
                }
                condition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        });

        t2 = new Thread(() -> {
            try {
                lock.lock();
                for (int aChar : ints) {
                    System.out.println(aChar);
                    condition.signal();
                    condition.await();
                }
                condition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        t1.start();
        t2.start();

        TimeUnit.SECONDS.sleep(2);

        System.out.println(t1.getState());
        System.out.println(t2.getState());

    }

    @Test
    public void test5() throws InterruptedException {
        final Exchanger<String> ex = new Exchanger<>();

        t1 = new Thread(() -> {
            for (char aChar : chars) {
                System.out.println(aChar);
                try {
                    ex.exchange("T1");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });


        t2 = new Thread(() -> {
            for (int aChar : ints) {
                System.out.println(aChar);
                try {
                    ex.exchange("T2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();
        t2.start();

        TimeUnit.SECONDS.sleep(2);

        System.out.println(t1.getState());
        System.out.println(t2.getState());

    }


}
